#include <sdk.h>
#include <wx/tokenzr.h>
#include <M_Code_StyledTextCtrl.h>
#include "classbrowser.h"
#include "nativeparser.h"
#include "parser/ccdebuginfo.h"

#define CC_CLASS_BROWSER_DEBUG_OUTPUT 0

int idMenuJumpToDeclaration = wxNewId();
int idMenuJumpToImplementation = wxNewId();
int idMenuRefreshTree = wxNewId();
int idCBViewInheritance = wxNewId();
int idCBExpandNS = wxNewId();
int idMenuForceReparse = wxNewId();
int idMenuDebugSmartSense = wxNewId();
int idCBNoSort = wxNewId();
int idCBSortByAlpabet = wxNewId();
int idCBSortByKind = wxNewId();
int idCBSortByScope = wxNewId();
int idCBSortByLine = wxNewId();
int idCBBottomTree = wxNewId();
int idThreadEvent = wxNewId();

BEGIN_EVENT_TABLE( ClassBrowser, wxPanel )
  EVT_TREE_ITEM_ACTIVATED( XRCID( "treeMembers" ), ClassBrowser::OnTreeItemDoubleClick )
  EVT_TREE_ITEM_RIGHT_CLICK( XRCID( "treeMembers" ), ClassBrowser::OnTreeItemRightClick )
  EVT_TREE_ITEM_ACTIVATED( XRCID( "treeAll" ), ClassBrowser::OnTreeItemDoubleClick )
  EVT_TREE_ITEM_RIGHT_CLICK( XRCID( "treeAll" ), ClassBrowser::OnTreeItemRightClick )
  EVT_TREE_ITEM_EXPANDING( XRCID( "treeAll" ), ClassBrowser::OnTreeItemExpanding )
  EVT_TREE_ITEM_COLLAPSING( XRCID( "treeAll" ), ClassBrowser::OnTreeItemCollapsing )
  EVT_TREE_SEL_CHANGED( XRCID( "treeAll" ), ClassBrowser::OnTreeSelChanged )
  EVT_TEXT_ENTER( XRCID( "cmbSearch" ), ClassBrowser::OnSearch )
  EVT_COMBOBOX( XRCID( "cmbSearch" ), ClassBrowser::OnSearch )
  EVT_BUTTON( XRCID( "btnSearch" ), ClassBrowser::OnSearch )
  EVT_CHOICE( XRCID( "cmbView" ), ClassBrowser::OnViewScope )
  EVT_MENU( idMenuJumpToDeclaration, ClassBrowser::OnJumpTo )
  EVT_MENU( idMenuJumpToImplementation, ClassBrowser::OnJumpTo )
  EVT_MENU( idMenuRefreshTree, ClassBrowser::OnRefreshTree )
  EVT_MENU( idMenuForceReparse, ClassBrowser::OnForceReparse )
  EVT_MENU( idCBViewInheritance, ClassBrowser::OnCBViewMode )
  EVT_MENU( idCBExpandNS, ClassBrowser::OnCBExpandNS )
  EVT_MENU( idMenuDebugSmartSense, ClassBrowser::OnDebugSmartSense )
  EVT_MENU( idCBNoSort, ClassBrowser::OnSetSortType )
  EVT_MENU( idCBSortByAlpabet, ClassBrowser::OnSetSortType )
  EVT_MENU( idCBSortByKind, ClassBrowser::OnSetSortType )
  EVT_MENU( idCBSortByScope, ClassBrowser::OnSetSortType )
  EVT_MENU( idCBSortByLine, ClassBrowser::OnSetSortType )
  EVT_MENU( idCBBottomTree, ClassBrowser::OnCBViewMode )
  EVT_COMMAND( idThreadEvent, wxEVT_COMMAND_ENTER, ClassBrowser::OnThreadEvent )
END_EVENT_TABLE()

ClassBrowser::ClassBrowser( wxWindow* parent, NativeParser* np ) :
  m_NativeParser( np ),
  m_TreeForPopupMenu( 0 ),
  m_Parser( 0L ),
  m_ClassBrowserSemaphore( 0,  1 ),
  m_ClassBrowserBuilderThread( 0 ) {
  wxXmlResource* xml_res = wxXmlResource::Get();
  wxString resPath = ConfigManager::GetDataFolder() + _T( "/classbrowser.xrc" );
  xml_res->Load( resPath );
  if( !xml_res->LoadPanel( this, parent, _T( "pnlCB" ) ) ) {
    return;
  }
  m_Search = XRCCTRL( *this, "cmbSearch", wxComboBox );
  m_CCTreeCtrl = XRCCTRL( *this, "treeAll", CCTreeCtrl );
  m_CCTreeCtrlBottom = XRCCTRL( *this, "treeMembers", CCTreeCtrl );
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "code_completion" ) );
  int filter = cfg->ReadInt( _T( "/browser_display_filter" ), bdfFile );
  XRCCTRL( *this, "cmbView", wxChoice )->SetSelection( filter );
  XRCCTRL( *this, "splitterWin", wxSplitterWindow )->SetMinSize( wxSize( -1, 200 ) );
  XRCCTRL( *this, "MainPanel", wxPanel )->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
}

ClassBrowser::~ClassBrowser() {
  int pos = XRCCTRL( *this, "splitterWin", wxSplitterWindow )->GetSashPosition();
  Manager::Get()->GetConfigManager( _T( "code_completion" ) )->Write( _T( "/splitter_pos" ), pos );
  SetParser( NULL );
  if( m_ClassBrowserBuilderThread ) {
    m_ClassBrowserBuilderThread->RequestTermination();
    m_ClassBrowserSemaphore.Post();
    m_ClassBrowserBuilderThread->Wait();
    delete m_ClassBrowserBuilderThread;
  }
}

void ClassBrowser::SetParser( ParserBase* parser ) {
  if( m_Parser == parser ) {
    return;
  }
  m_Parser = parser;
  if( m_Parser ) {
    int sel = XRCCTRL( *this, "cmbView", wxChoice )->GetSelection();
    BrowserDisplayFilter filter = static_cast<BrowserDisplayFilter>( sel );
    if( !m_NativeParser->IsParserPerWorkspace() && filter == bdfWorkspace ) {
      filter = bdfProject;
    }
    m_Parser->ClassBrowserOptions().displayFilter = filter;
    m_Parser->WriteOptions();
    UpdateClassBrowserView();
  }
}

void ClassBrowser::UpdateSash() {
  int pos = Manager::Get()->GetConfigManager( _T( "code_completion" ) )->ReadInt( _T( "/splitter_pos" ), 250 );
  XRCCTRL( *this, "splitterWin", wxSplitterWindow )->SetSashPosition( pos, false );
  XRCCTRL( *this, "splitterWin", wxSplitterWindow )->Refresh();
}

void ClassBrowser::UpdateClassBrowserView( bool checkHeaderSwap ) {
  wxString oldActiveFilename( m_ActiveFilename );
  m_ActiveFilename.Clear();
  if( !m_Parser || Manager::IsAppShuttingDown() ) {
    return;
  }
  M_Code_Editor* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( editor ) {
    m_ActiveFilename = editor->GetFilename();
  }
  if( checkHeaderSwap ) {
    wxString oldShortName = oldActiveFilename.AfterLast( _T( '/' ) );
    if( oldShortName.Find( _T( '.' ) ) != wxNOT_FOUND ) {
      oldShortName = oldShortName.BeforeLast( _T( '.' ) );
    }
    wxString newShortName = m_ActiveFilename.AfterLast( _T( '/' ) );
    if( newShortName.Find( _T( '.' ) ) != wxNOT_FOUND ) {
      newShortName = newShortName.BeforeLast( _T( '.' ) );
    }
    if( oldShortName.IsSameAs( newShortName ) ) {
      return;
    }
  }
  M_Code_Project* activeProject = 0;
  if( !m_NativeParser->IsParserPerWorkspace() ) {
    activeProject = m_NativeParser->GetProjectByParser( m_Parser );
  } else {
    activeProject = m_NativeParser->GetCurrentProject();
  }
  if( !activeProject ) {
    // TODO (m#1#2019年07月19日): 类浏览器返回
    return;
  }
  ThreadedBuildTree( activeProject );
  wxSplitterWindow* splitter = XRCCTRL( *this, "splitterWin", wxSplitterWindow );
  if( m_Parser->ClassBrowserOptions().treeMembers ) {
    splitter->SplitHorizontally( m_CCTreeCtrl, m_CCTreeCtrlBottom );
    m_CCTreeCtrlBottom->Show( true );
  } else {
    splitter->Unsplit();
    m_CCTreeCtrlBottom->Show( false );
  }
}

void ClassBrowser::ShowMenu( wxTreeCtrl* tree, wxTreeItemId id, M_Code_unused const wxPoint& pt ) {
  m_TreeForPopupMenu = tree;
  if( !id.IsOk() || !m_Parser ) {
    return;
  }
  wxString caption;
  wxMenu* menu = new wxMenu( wxEmptyString );
  CCTreeCtrlData* ctd = ( CCTreeCtrlData* )tree->GetItemData( id );
  if( ctd && ctd->m_Token ) {
    switch( ctd->m_Token->m_TokenKind ) {
      case tkConstructor:
      case tkDestructor:
      case tkFunction:
        if( ctd->m_Token->m_ImplLine != 0 && !ctd->m_Token->GetImplFilename().IsEmpty() ) {
          menu->Append( idMenuJumpToImplementation, _T( "Jump to &implementation" ) );
        }
      case tkNamespace:
      case tkClass:
      case tkEnum:
      case tkTypedef:
      case tkVariable:
      case tkEnumerator:
      case tkMacroDef:
      case tkMacroUse:
      case tkAnyContainer:
      case tkAnyFunction:
      case tkUndefined:
      default:
        menu->Append( idMenuJumpToDeclaration, _T( "Jump to &declaration" ) );
    }
  }
  const BrowserOptions& options = m_Parser->ClassBrowserOptions();
  if( tree == m_CCTreeCtrl ) {
    menu->AppendCheckItem( idCBViewInheritance, _T( "Show inherited members" ) );
    menu->AppendCheckItem( idCBExpandNS, _T( "Auto-expand namespaces" ) );
    menu->Append( idMenuRefreshTree, _T( "&Refresh tree" ) );
    if( id == m_CCTreeCtrl->GetRootItem() ) {
      menu->Append( idMenuForceReparse, _T( "Re-parse now" ) );
    }
    if( wxGetKeyState( WXK_CONTROL ) && wxGetKeyState( WXK_SHIFT ) ) {
      menu->AppendCheckItem( idMenuDebugSmartSense, _T( "Debug SmartSense" ) );
      menu->Check( idMenuDebugSmartSense, s_DebugSmartSense );
    }
    menu->Check( idCBViewInheritance, m_Parser ? options.showInheritance : false );
    menu->Check( idCBExpandNS, m_Parser ? options.expandNS : false );
  }
  menu->AppendCheckItem( idCBNoSort, _T( "Do not sort" ) );
  menu->AppendCheckItem( idCBSortByAlpabet, _T( "Sort alphabetically" ) );
  menu->AppendCheckItem( idCBSortByKind, _T( "Sort by kind" ) );
  menu->AppendCheckItem( idCBSortByScope, _T( "Sort by access" ) );
  menu->AppendCheckItem( idCBSortByLine, _T( "Sort by line" ) );
  const BrowserSortType& bst = options.sortType;
  switch( bst ) {
    case bstAlphabet:
      menu->Check( idCBSortByAlpabet, true );
      break;
    case bstKind:
      menu->Check( idCBSortByKind, true );
      break;
    case bstScope:
      menu->Check( idCBSortByScope, true );
      break;
    case bstLine:
      menu->Check( idCBSortByLine, true );
      break;
    case bstNone:
    default:
      menu->Check( idCBNoSort, true );
      break;
  }
  menu->AppendCheckItem( idCBBottomTree, _T( "Display bottom tree" ) );
  menu->Check( idCBBottomTree, options.treeMembers );
  if( menu->GetMenuItemCount() != 0 ) {
    PopupMenu( menu );
  }
  delete menu;
}

bool ClassBrowser::FoundMatch( const wxString& search, wxTreeCtrl* tree, const wxTreeItemId& item ) {
  ClassTreeData* ctd = static_cast<ClassTreeData*>( tree->GetItemData( item ) );
  if( ctd && ctd->GetToken() ) {
    const Token* token = ctd->GetToken();
    if( token->m_Name.Lower().StartsWith( search )
        || token->m_Name.Lower().StartsWith( _T( '~' ) + search ) ) {
      return true;
    }
  }
  return false;
}

wxTreeItemId ClassBrowser::FindNext( const wxString& search, wxTreeCtrl* tree, const wxTreeItemId& start ) {
  wxTreeItemId ret;
  if( !start.IsOk() ) {
    return ret;
  }
  ret = tree->GetNextSibling( start );
  if( ret.IsOk() ) {
    return ret;
  }
  return FindNext( search, tree, tree->GetItemParent( start ) );
}

wxTreeItemId ClassBrowser::FindChild( const wxString& search, wxTreeCtrl* tree, const wxTreeItemId& start, bool recurse, bool partialMatch ) {
  if( !tree ) {
    return wxTreeItemId();
  }
  wxTreeItemIdValue cookie;
  wxTreeItemId res = tree->GetFirstChild( start, cookie );
  while( res.IsOk() ) {
    wxString text = tree->GetItemText( res );
    if( ( !partialMatch && text == search )
        || ( partialMatch && text.StartsWith( search ) ) ) {
      return res;
    }
    if( recurse && tree->ItemHasChildren( res ) ) {
      res = FindChild( search, tree, res, true, partialMatch );
      if( res.IsOk() ) {
        return res;
      }
    }
    res = m_CCTreeCtrl->GetNextChild( start, cookie );
  }
  res.Unset();
  return res;
}

bool ClassBrowser::RecursiveSearch( const wxString& search, wxTreeCtrl* tree, const wxTreeItemId& parent, wxTreeItemId& result ) {
  if( !parent.IsOk() || !tree ) {
    return false;
  }
  if( FoundMatch( search, tree, parent ) ) {
    result = parent;
    return true;
  }
  wxTreeItemIdValue cookie;
  wxTreeItemId child = tree->GetFirstChild( parent, cookie );
  if( !child.IsOk() ) {
    return RecursiveSearch( search, tree, FindNext( search, tree, parent ), result );
  }
  while( child.IsOk() ) {
    if( FoundMatch( search, tree, child ) ) {
      result = child;
      return true;
    }
    if( tree->ItemHasChildren( child ) ) {
      if( RecursiveSearch( search, tree, child, result ) ) {
        return true;
      }
    }
    child = tree->GetNextChild( parent, cookie );
  }
  return RecursiveSearch( search, tree, FindNext( search, tree, parent ), result );
}


void ClassBrowser::OnTreeItemRightClick( wxTreeEvent& event ) {
  wxTreeCtrl* tree = ( wxTreeCtrl* )event.GetEventObject();
  if( !tree ) {
    return;
  }
  tree->SelectItem( event.GetItem() );
  ShowMenu( tree, event.GetItem(), event.GetPoint() );
}

void ClassBrowser::OnJumpTo( wxCommandEvent& event ) {
  wxTreeCtrl* tree = m_TreeForPopupMenu;
  if( !tree || !m_Parser ) {
    return;
  }
  wxTreeItemId id = tree->GetSelection();
  CCTreeCtrlData* ctd = ( CCTreeCtrlData* )tree->GetItemData( id );
  if( ctd ) {
    wxFileName fname;
    if( event.GetId() == idMenuJumpToImplementation ) {
      fname.Assign( ctd->m_Token->GetImplFilename() );
    } else
    { fname.Assign( ctd->m_Token->GetFilename() ); }
    M_Code_Project* project = nullptr;
    if( !m_NativeParser->IsParserPerWorkspace() ) {
      project = m_NativeParser->GetProjectByParser( m_Parser );
    } else {
      project = m_NativeParser->GetCurrentProject();
    }
    wxString base;
    if( project ) {
      base = project->GetBasePath();
      NormalizePath( fname, base );
    } else {
      const wxArrayString& incDirs = m_Parser->GetIncludeDirs();
      for( size_t i = 0; i < incDirs.GetCount(); ++i ) {
        if( NormalizePath( fname, incDirs.Item( i ) ) ) {
          break;
        }
      }
    }
    M_Code_Editor* ed = Manager::Get()->GetEditorManager()->Open( fname.GetFullPath() );
    if( ed ) {
      int line;
      if( event.GetId() == idMenuJumpToImplementation ) {
        line = ctd->m_Token->m_ImplLine - 1;
      } else {
        line = ctd->m_Token->m_Line - 1;
      }
      ed->GotoTokenPosition( line, ctd->m_Token->m_Name );
    }
  }
}

void ClassBrowser::OnTreeItemDoubleClick( wxTreeEvent& event ) {
  wxTreeCtrl* wx_tree = ( wxTreeCtrl* )event.GetEventObject();
  if( !wx_tree || !m_Parser ) {
    return;
  }
  wxTreeItemId id = event.GetItem();
  CCTreeCtrlData* ctd = ( CCTreeCtrlData* )wx_tree->GetItemData( id );
  if( ctd && ctd->m_Token ) {
    if( wxGetKeyState( WXK_CONTROL ) && wxGetKeyState( WXK_SHIFT ) ) {
      CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
      CCDebugInfo info( wx_tree, m_Parser, ctd->m_Token );
      info.ShowModal();
      CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
      return;
    }
    bool toImp = false;
    switch( ctd->m_Token->m_TokenKind ) {
      case tkConstructor:
      case tkDestructor:
      case tkFunction:
        if( ctd->m_Token->m_ImplLine != 0 && !ctd->m_Token->GetImplFilename().IsEmpty() ) {
          toImp = true;
        }
        break;
      case tkNamespace:
      case tkClass:
      case tkEnum:
      case tkTypedef:
      case tkVariable:
      case tkEnumerator:
      case tkMacroDef:
      case tkMacroUse:
      case tkAnyContainer:
      case tkAnyFunction:
      case tkUndefined:
      default:
        break;
    }
    wxFileName fname;
    if( toImp ) {
      fname.Assign( ctd->m_Token->GetImplFilename() );
    } else
    { fname.Assign( ctd->m_Token->GetFilename() ); }
    M_Code_Project* project = nullptr;
    if( !m_NativeParser->IsParserPerWorkspace() ) {
      project = m_NativeParser->GetProjectByParser( m_Parser );
    } else
    { project = m_NativeParser->GetCurrentProject(); }
    wxString base;
    if( project ) {
      base = project->GetBasePath();
      NormalizePath( fname, base );
    } else {
      const wxArrayString& incDirs = m_Parser->GetIncludeDirs();
      for( size_t i = 0; i < incDirs.GetCount(); ++i ) {
        if( NormalizePath( fname, incDirs.Item( i ) ) ) {
          break;
        }
      }
    }
    M_Code_Editor* ed = Manager::Get()->GetEditorManager()->Open( fname.GetFullPath() );
    if( ed ) {
      int line;
      if( toImp ) {
        line = ctd->m_Token->m_ImplLine - 1;
      } else
      { line = ctd->m_Token->m_Line - 1; }
      ed->GotoTokenPosition( line, ctd->m_Token->m_Name );
    }
  }
}

void ClassBrowser::OnRefreshTree( M_Code_unused wxCommandEvent& event ) {
  UpdateClassBrowserView();
}

void ClassBrowser::OnForceReparse( M_Code_unused wxCommandEvent& event ) {
  if( m_NativeParser ) {
    m_NativeParser->ReparseCurrentProject();
  }
}

void ClassBrowser::OnCBViewMode( wxCommandEvent& event ) {
  if( !m_Parser ) {
    return;
  }
  BrowserOptions& options = m_Parser->ClassBrowserOptions();
  if( event.GetId() == idCBViewInheritance ) {
    options.showInheritance = event.IsChecked();
  }
  if( event.GetId() == idCBExpandNS ) {
    options.expandNS = event.IsChecked();
  }
  if( event.GetId() == idCBBottomTree ) {
    options.treeMembers = event.IsChecked();
  }
  m_Parser->WriteOptions();
  UpdateClassBrowserView();
}

void ClassBrowser::OnCBExpandNS( wxCommandEvent& event ) {
  if( !m_Parser ) {
    return;
  }
  if( event.GetId() == idCBExpandNS ) {
    m_Parser->ClassBrowserOptions().expandNS = event.IsChecked();
  }
  m_Parser->WriteOptions();
  UpdateClassBrowserView();
}

void ClassBrowser::OnViewScope( wxCommandEvent& event ) {
  int sel = event.GetSelection();
  if( m_Parser ) {
    BrowserDisplayFilter filter = static_cast<BrowserDisplayFilter>( sel );
    if( !m_NativeParser->IsParserPerWorkspace() && filter == bdfWorkspace ) {
      msging( _T( "This feature is not supported in combination with\n"
                  "the option \"one parser per whole workspace\"." ),
              _T( "Information" ), wxICON_INFORMATION );
      filter = bdfProject;
      XRCCTRL( *this, "cmbView", wxChoice )->SetSelection( filter );
    }
    m_Parser->ClassBrowserOptions().displayFilter = filter;
    m_Parser->WriteOptions();
    UpdateClassBrowserView();
  } else {
    Manager::Get()->GetConfigManager( _T( "code_completion" ) )->Write( _T( "/browser_display_filter" ), sel );
  }
}

void ClassBrowser::OnDebugSmartSense( M_Code_unused wxCommandEvent& event ) {
  s_DebugSmartSense = !s_DebugSmartSense;
}

void ClassBrowser::OnSetSortType( wxCommandEvent& event ) {
  BrowserSortType bst;
  if( event.GetId() == idCBSortByAlpabet ) {
    bst = bstAlphabet;
  } else if( event.GetId() == idCBSortByKind ) {
    bst = bstKind;
  } else if( event.GetId() == idCBSortByScope ) {
    bst = bstScope;
  } else if( event.GetId() == idCBSortByLine ) {
    bst = bstLine;
  } else {
    bst = bstNone;
  }
  if( m_Parser ) {
    m_Parser->ClassBrowserOptions().sortType = bst;
    m_Parser->WriteOptions();
    UpdateClassBrowserView();
  } else {
    Manager::Get()->GetConfigManager( _T( "code_completion" ) )->Write( _T( "/browser_sort_type" ), ( int )bst );
  }
}

void ClassBrowser::OnSearch( M_Code_unused wxCommandEvent& event ) {
  wxString search = m_Search->GetValue();
  if( search.IsEmpty() || !m_Parser ) {
    return;
  }
  TokenTree* tree = m_Parser->GetTokenTree();
  TokenIdxSet result;
  size_t count = 0;
  {
    CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
    count = tree->FindMatches( search, result, false, true );
    CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  }
  const Token* token = 0;
  if( count == 0 ) {
    msging( _T( "No matches were found: " ) + search,
            _T( "Search failed" ), wxICON_INFORMATION );
    return;
  } else if( count == 1 ) {
    CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
    token = tree->at( *result.begin() );
    CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  } else if( count > 1 ) {
    wxArrayString selections;
    wxArrayInt int_selections;
    for( TokenIdxSet::iterator it = result.begin(); it != result.end(); ++it ) {
      CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
      const Token* sel = tree->at( *it );
      if( sel ) {
        selections.Add( sel->DisplayName() );
        int_selections.Add( *it );
      }
      CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
    }
    if( selections.GetCount() > 1 ) {
      int sel = cbGetSingleChoiceIndex( _T( "Please make a selection:" ), _T( "Multiple matches" ), selections,
                                        Manager::Get()->GetAppWindow(), wxSize( 400, 400 ) );
      if( sel == -1 ) {
        return;
      }
      CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
      token = tree->at( int_selections[sel] );
      CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
    } else if( selections.GetCount() == 1 ) {
      CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
      token = tree->at( int_selections[0] );
      CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
    }
  }
  if( token ) {
    if( m_Search->FindString( token->m_Name ) == wxNOT_FOUND ) {
      m_Search->Append( token->m_Name );
    }
    if( token->m_ParentIndex == -1 && !( token->m_TokenKind & tkAnyContainer ) ) {
      wxTreeItemIdValue cookie;
      wxTreeItemId res = m_CCTreeCtrl->GetFirstChild( m_CCTreeCtrl->GetRootItem(), cookie );
      while( res.IsOk() ) {
        CCTreeCtrlData* data = ( CCTreeCtrlData* )m_CCTreeCtrl->GetItemData( res );
        if( data && ( data->m_SpecialFolder & ( sfGFuncs | sfGVars | sfPreproc | sfTypedef ) ) ) {
          m_CCTreeCtrl->SelectItem( res );
          wxTreeItemId srch = FindChild( token->m_Name, m_CCTreeCtrlBottom, m_CCTreeCtrlBottom->GetRootItem(), false, true );
          if( srch.IsOk() ) {
            m_CCTreeCtrlBottom->SelectItem( srch );
            return;
          }
        }
        res = m_CCTreeCtrl->GetNextChild( m_CCTreeCtrl->GetRootItem(), cookie );
      }
      return;
    }
    wxTreeItemId start = m_CCTreeCtrl->GetRootItem();
    wxStringTokenizer tkz( token->GetNamespace(), _T( ":" ) );
    while( tkz.HasMoreTokens() ) {
      wxString part = tkz.GetNextToken();
      if( !part.IsEmpty() ) {
        m_CCTreeCtrl->Expand( start );
        wxTreeItemId res = FindChild( part, m_CCTreeCtrl, start );
        if( !res.IsOk() ) {
          break;
        }
        start = res;
      }
    }
    m_CCTreeCtrl->Expand( start );
    m_CCTreeCtrl->SelectItem( start );
    wxTreeItemId res = FindChild( token->m_Name, m_CCTreeCtrl, start );
    if( res.IsOk() ) {
      m_CCTreeCtrl->SelectItem( res );
    } else {
      res = FindChild( token->m_Name, m_CCTreeCtrlBottom, m_CCTreeCtrlBottom->GetRootItem(), true, true );
      if( res.IsOk() ) {
        m_CCTreeCtrlBottom->SelectItem( res );
      }
    }
  }
}

void ClassBrowser::ThreadedBuildTree( M_Code_Project* activeProject ) {
  if( Manager::IsAppShuttingDown() || !m_Parser ) {
    return;
  }
  bool thread_needs_run = false;
  if( !m_ClassBrowserBuilderThread ) {
    m_ClassBrowserBuilderThread = new ClassBrowserBuilderThread( this, m_ClassBrowserSemaphore );
    m_ClassBrowserBuilderThread->Create();
    thread_needs_run = true;
  }
  bool thread_needs_resume = false;
  while( !thread_needs_run
         &&  m_ClassBrowserBuilderThread->IsAlive()
         &&  m_ClassBrowserBuilderThread->IsRunning()
         && !m_ClassBrowserBuilderThread->IsPaused() ) {
    thread_needs_resume = true;
    m_ClassBrowserBuilderThread->Pause();
    wxMilliSleep( 20 );
  }
  m_ClassBrowserBuilderThread->Init( m_NativeParser,
                                     m_CCTreeCtrl,
                                     m_CCTreeCtrlBottom,
                                     m_ActiveFilename,
                                     activeProject,
                                     m_Parser->ClassBrowserOptions(),
                                     m_Parser->GetTokenTree(),
                                     idThreadEvent );
  if( thread_needs_run ) {
    m_ClassBrowserBuilderThread->Run();
    m_ClassBrowserSemaphore.Post();
  } else if( thread_needs_resume ) {
    if( m_ClassBrowserBuilderThread->IsAlive() && m_ClassBrowserBuilderThread->IsPaused() ) {
      m_ClassBrowserBuilderThread->Resume();
      m_ClassBrowserSemaphore.Post();
    }
  }
}

void ClassBrowser::OnTreeItemExpanding( wxTreeEvent& event ) {
  if( m_ClassBrowserBuilderThread ) {
    m_ClassBrowserBuilderThread->ExpandItem( event.GetItem() );
  }
  event.Allow();
}

void ClassBrowser::OnTreeItemCollapsing( wxTreeEvent& event ) {
  if( m_ClassBrowserBuilderThread ) {
    m_ClassBrowserBuilderThread->CollapseItem( event.GetItem() );
  }
  event.Allow();
}

void ClassBrowser::OnTreeSelChanged( wxTreeEvent& event ) {
  if( !::wxIsMainThread() ) {
    return;
  }
  if( m_ClassBrowserBuilderThread && m_Parser && m_Parser->ClassBrowserOptions().treeMembers ) {
    m_ClassBrowserBuilderThread->SelectItem( event.GetItem() );
  }
  event.Allow();
}

void ClassBrowser::OnThreadEvent( wxCommandEvent& event ) {
  ClassBrowserBuilderThread::EThreadEvent query =
    static_cast<ClassBrowserBuilderThread::EThreadEvent>( event.GetInt() );
  switch( query ) {
    case ClassBrowserBuilderThread::selectItemRequired: {
      if( m_ClassBrowserBuilderThread && m_Parser && m_Parser->ClassBrowserOptions().treeMembers ) {
        m_ClassBrowserBuilderThread->SelectItemRequired();
      }
      break;
    }
    case ClassBrowserBuilderThread::buildTreeStart: {
      break;
    }
    case ClassBrowserBuilderThread::buildTreeEnd: {
      break;
    }
    default:
      break;
  }
}
